﻿package render3d.adapters 
{
	import exey.utils.BitmapUtils;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.geom.Matrix3D;
	import flash.geom.Vector3D;
	import render3d.adapters.abstract.Render3DAdapterAbstract;
	import render3d.CrossEngineObject3D;
	import render3d.interfaces.IRender3DAdapter;
	import render3d.interfaces.IRender3DForObject3D;
	import sandy.core.Scene3D;
	import sandy.core.scenegraph.ATransformable;
	import sandy.core.scenegraph.Camera3D;
	import sandy.core.scenegraph.Group;
	import sandy.core.scenegraph.Sprite2D;
	import sandy.materials.Appearance;
	import sandy.materials.BitmapMaterial;
	import sandy.primitive.Plane3D;
	import sandy.primitive.PrimitiveMode;
	import sandy.primitive.SkyBox;
	
	/**
	 * Sandy3D 3.1.2 Adapter
	 * @author Exey Panteleev
	 */
	public class Sandy3DAdapter extends Render3DAdapterAbstract implements IRender3DAdapter, IRender3DForObject3D
	{
		public var useRenderingCache:Boolean = true;
		public var camera:Camera3D;
		public var scene:Scene3D;
		
		public function Sandy3DAdapter() 
		{
			initScene();
		}
		
		public function initScene():void 
		{
			var rootNode:Group = new Group("root");
			camera = new Camera3D(800, 600);
			camera.fov = 60;
			scene = new Scene3D("mainScene", viewContainer, camera, rootNode);
		}
		
		//--------------------------------------------------------------------------
		//
		//  Camera
		//
		//--------------------------------------------------------------------------	
		
		public function setCameraPosition(x:Number = NaN, y:Number = NaN, z:Number = NaN):void 
		{
			if (!isNaN(x)) camera.x = x;
			if (!isNaN(y)) camera.y = y;
			if (!isNaN(z)) camera.z = z;
		}
		
		public function moveCamera(forwardSpeed:Number, rightSpeed:Number, jumpSpeed:Number):void 
		{
			camera.x += rightSpeed;
			camera.y -= forwardSpeed;
			camera.z += jumpSpeed;
		}
		
		public function rotateCamera(x:Number = NaN, y:Number = NaN, z:Number = NaN):void
		{
			if (!isNaN(x)) camera.tilt = -x;
			if (!isNaN(y)) camera.rotateY = y;
			if (!isNaN(z)) camera.rotateZ = z;
		}		
		
		public function lookCamera(mouseX:Number, mouseY:Number):void 
		{
			camera.rotateZ += mouseX;
			camera.tilt += mouseY;
		}
		
		public function getCameraTransformation():Matrix3D
		{
			var matrix:Matrix3D = new Matrix3D();
			var components:Vector.<Vector3D> = new Vector.<Vector3D>();
			components[0] = new Vector3D(camera.x, camera.y, camera.z);
			components[1] = new Vector3D(camera.tilt* toRADIANS, camera.rotateY * toRADIANS, camera.rotateZ * toRADIANS);
			components[2] = new Vector3D(1, 1, 1);
			matrix.recompose(components);
			return matrix;
		}
		
		public function getCameraHeight():Number 
		{
			return camera.z;
		}
		
		public function getCameraPanAngle():Number 
		{
			return -camera.rotateZ*toRADIANS;
		}
		
		//--------------------------------------------------------------------------
		//
		//  3D Objects 
		//
		//--------------------------------------------------------------------------
		
		public function createPlane(bitmapData:BitmapData, width:int, height:int, type:String = ""):CrossEngineObject3D 
		{
			var material:BitmapMaterial = new BitmapMaterial(bitmapData);
			var plane:Plane3D = new Plane3D( null, height, width, 4, 4, Plane3D.XY_ALIGNED, PrimitiveMode.QUAD );
			plane.rotateX = -180
			plane.appearance = new Appearance(material, null);
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, plane);
			return crossEngineObject3D;				
		}
		
		public function createSkybox(backTexture:BitmapData, leftTexture:BitmapData, frontTexture:BitmapData, rightTexture:BitmapData, topTexture:BitmapData, bottomTexture:BitmapData):CrossEngineObject3D
		{
			var backMaterial:BitmapMaterial = new BitmapMaterial(backTexture);
			var leftMaterial:BitmapMaterial = new BitmapMaterial(leftTexture);
			var frontMaterial:BitmapMaterial = new BitmapMaterial(frontTexture);
			var rightMaterial:BitmapMaterial = new BitmapMaterial(rightTexture);
			var topMaterial:BitmapMaterial = new BitmapMaterial(BitmapUtils.rotateRectangular(topTexture, Math.PI)); // rotated
			var bottomMaterial:BitmapMaterial = new BitmapMaterial(bottomTexture);
			var skybox:SkyBox = new SkyBox("pano", 512*10, 3, 3)
			skybox.x = skybox.y = 1280;
			skybox.back.appearance = new Appearance( backMaterial );
			skybox.left.appearance = new Appearance( leftMaterial );
			skybox.front.appearance = new Appearance( frontMaterial );
			skybox.right.appearance = new Appearance( rightMaterial );
			skybox.top.appearance = new Appearance( topMaterial );
			skybox.bottom.appearance = new Appearance( bottomMaterial );
			skybox.rotateX = 90;
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, skybox);
			return crossEngineObject3D;
		}
		
		public function createSprite3D(bitmapData:BitmapData, type:String = ""):CrossEngineObject3D 
		{
			var sprite3D:Sprite2D = new Sprite2D("", new Bitmap(bitmapData), 1);
			var crossEngineObject3D:CrossEngineObject3D = new CrossEngineObject3D(this, sprite3D);
			return crossEngineObject3D;	
		}
		
		public function changeTexture( bitmapData:BitmapData, targetObject3D:* ):void 
		{
			if (targetObject3D is Sprite2D)
				Sprite2D(targetObject3D).content = new Bitmap(bitmapData)
		}		
		
		//--------------------------------------------------------------------------
		//
		//  Common
		//
		//--------------------------------------------------------------------------
		
		public function update():void 
		{
			scene.render()
		}
		
		public function resize(stageWidth:int, stageHeight:int):void 
		{
		
		}
		
		//--------------------------------------------------------------------------
		//
		//  INTERFACE render3d.interfaces.IRender3DForObject3D
		//
		//--------------------------------------------------------------------------		
		
		public function copyTransformation(sourceMatrix3D:Matrix3D, targetObject3D:*):void 
		{
			var components:Vector.<Vector3D> = sourceMatrix3D.decompose();
			
			ATransformable(targetObject3D).x = components[0].x;
			ATransformable(targetObject3D).y = components[0].y;
			ATransformable(targetObject3D).z = components[0].z;
			
			ATransformable(targetObject3D).rotateX = components[1].x*toDEGREES-90;
			ATransformable(targetObject3D).rotateY = components[1].y*toDEGREES;
			ATransformable(targetObject3D).rotateZ = components[1].z*toDEGREES;
			
			ATransformable(targetObject3D).scaleX = components[2].x;
			ATransformable(targetObject3D).scaleY = components[2].y;
			ATransformable(targetObject3D).scaleZ = components[2].z;
		}
		
		public function copyPosition(sourceMatrix3D:Matrix3D, targetObject3D:*):void 
		{
			var components:Vector.<Vector3D> = sourceMatrix3D.decompose();
			ATransformable(targetObject3D).x = components[0].x;
			ATransformable(targetObject3D).y = components[0].y;
			ATransformable(targetObject3D).z = components[0].z;
		}
		
		//--------------------------------------------------------------------------
		//
		//  INTERFACE render3d.interfaces.abstract.IRender3DAbstract
		//
		//--------------------------------------------------------------------------
		
		public function addChild(child:CrossEngineObject3D):void 
		{
			scene.root.addChild(child.object3D);
		}
		
		public function removeChild(child:CrossEngineObject3D):void 
		{
			ATransformable(child.object3D).remove();
		}
	}
}